---
title: Custom Shaders  
---

Similar to functions, classes, and properties in TypeScript, shader code also has its own set of APIs and配套的 [UIScript](/en/docs/graphics/material/shaderLab/script). This article will guide you through customizing your shaders using these APIs and [ShaderLab](/en/docs/graphics/material/shaderLab/overview).

## Quick Start

We'll start with the `Unlit template` to introduce our shader API. Follow the steps below to create a Unlit shader:

<img
  src="https://gw.alipayobjects.com/zos/OasisHub/9abd1026-4e4d-4994-b36a-f964375c38cb/image-20240731105324320.png"
  style={{ zoom: "50%" }}
/>

The engine will automatically generate the shader file and [UIScript](/en/docs/graphics/material/shaderLab/script) file for you:

<img
  src="https://gw.alipayobjects.com/zos/OasisHub/6351fa81-5159-4469-bd95-8f21a8f2f4ac/image-20250124162909194.png"
  style={{ zoom: "50%" }}
/>

By default, the Unlit template includes skinning calculations and a Shadow Pass. As shown below, skeletal animations and shadows render correctly:

<Image src="https://gw.alipayobjects.com/zos/OasisHub/6e7f7d40-e54c-45bc-a915-dfbfb26c2c74/2024-08-01%25252017.01.06.gif" />

Key code includes using `UsePass "pbr/Default/ShadowCaster"` to enable shadow mapping and `getSkinMatrix` to animate the mesh:

```ts showLineNumbers {1,11-15} /getSkinMatrix/
UsePass "pbr/Default/ShadowCaster"

Pass "Example" {
	#include "Skin.glsl"

	Varyings vert(Attributes attr) {
		Varyings v;

		vec4 position = vec4(attr.POSITION, 1.0);

		// Skin
		#ifdef RENDERER_HAS_SKIN
		  mat4 skinMatrix = getSkinMatrix(attr);
		  position = skinMatrix * position;
		#endif

		gl_Position = renderer_MVPMat * position;
		v.uv = attr.TEXCOORD_0;

		return v;
	}
}
```

Unlit shaders are unaffected by lighting by default. To make the output respond to lighting, use APIs from `Light.glsl`:

```ts showLineNumbers {1,4} /getDirectLight/
#include "Light.glsl"

// Demo: Calculate only the first directional light
DirectLight light = getDirectLight(0);
float dotNL = saturate(dot(v.normalWS, -light.direction));
baseColor.rgb *= dotNL * light.color;
```

<Image src="https://gw.alipayobjects.com/zos/OasisHub/a552c86f-6a59-4765-89ff-fac3b38aa9d2/2024-08-01%25252017.06.14.gif" />

While vertex color calculations, normal maps, and [ambient lighting](/en/docs/graphics/light/ambient) can also be implemented, we recommend using the `PBR template` instead of the `Unlit template`. The PBR template already includes these features and provides a more comprehensive lighting model (e.g., anisotropy, Clear Coat) with macro-based extensions.

## PBR Template

Create a `PBR Shader template` and bind it to your material. The material panel will now include settings for base properties, metallic/roughness, anisotropy, normals, emissive, occlusion, and Clear Coat, all responsive to direct and ambient lighting:

<img
  src="https://gw.alipayobjects.com/zos/OasisHub/1bb43cac-ca21-4342-a6ea-a19324eaf12d/image-20240801174338560.png"
  style={{ zoom: "50%" }}
/>

<Image src="https://gw.alipayobjects.com/zos/OasisHub/f8f09e89-e14d-481e-a328-eed491f41e79/image-20240801174216595.png" />

<Image src="https://gw.alipayobjects.com/zos/OasisHub/65a7f2ec-ffd5-45cf-9508-8d951b995e3d/2024-08-01%25252017.40.02.gif" />

### Overriding the Lighting Model

1. Create a `DemoPass.glsl` file and include it in your main shader:

```ts showLineNumbers {7-8}
// PBRShader.gs
SubShader "Default" {
	Pass "Forward Pass" {
	  VertexShader = PBRVertex;
	  FragmentShader = PBRFragment;

	  // #include "ForwardPassPBR.glsl"
	  #include "./DemoPass.glsl"
	}
}
```

2. Modify the lighting model in `DemoPass.glsl` (demo shows direct light changes):

```ts showLineNumbers {7-8}
// DemoPass.glsl
#include "Common.glsl"
#include "Fog.glsl"

#include "AttributesPBR.glsl"
#include "VaryingsPBR.glsl"
// #include "LightDirectPBR.glsl"
#include "DemoLight.glsl"

#include "LightIndirectPBR.glsl"

#include "VertexPBR.glsl"
#include "FragmentPBR.glsl"
```

3. Override the specular reflection model using `FUNCTION_SPECULAR_LOBE` (example uses thin-film interference):

```ts showLineNumbers {2,5,16} /specularLobe_iridescence/
// DemoLight.glsl
#define FUNCTION_SPECULAR_LOBE specularLobe_iridescence

#include "BRDF.glsl"
#include "./IridescenceFunction.glsl"

void specularLobe_iridescence(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, vec3 incidentDirection, vec3 attenuationIrradiance, inout vec3 specularColor){
 vec3 thin = DirectBDRFIridescence(surfaceData, incidentDirection, brdfData);
 vec3 BRDF_Specular = BRDF_Specular_GGX( incidentDirection, surfaceData, surfaceData.normal, brdfData.specularColor, brdfData.roughness);
 vec3 factor = mix(BRDF_Specular, thin, material_Iridescence);
 specularColor += attenuationIrradiance * factor;
}

#include "LightDirectPBR.glsl"
```

<Image src="https://gw.alipayobjects.com/zos/OasisHub/ad18c98d-d1a5-47fd-b5c8-882908c249a2/2024-08-01%25252018.51.36.gif" />

## Common APIs

### Usage Example
```glsl
#include "Common.glsl"

float f2 = pow2(0.5);
```

### Common
Provides macros like `PI` and utility functions (`gammaToLinear`, `pow2`). See [source](https://github.com/galacean/engine/blob/main/packages/shader-shaderlab/src/shaders/Common.glsl).

### Fog
Depth fog calculation:
```glsl
vec4 fog(vec4 color, vec3 positionVS);
```

### Transform
System variables for model/view/world space:
```glsl
mat4 renderer_LocalMat;
mat4 renderer_ModelMat;
mat4 camera_ViewMat;
mat4 camera_ProjMat;
mat4 renderer_MVMat;
mat4 renderer_MVPMat;
mat4 renderer_NormalMat;

vec3 camera_Position;
vec3 camera_Forward;
vec4 camera_ProjectionParams;
```

### Light
Access engine lighting data:
```glsl
DirectLight getDirectLight(int index);
PointLight getPointLight(int index);
SpotLight getSpotLight(int index);

EnvMapLight scene_EnvMapLight;

#ifdef SCENE_USE_SH
    vec3 scene_EnvSH[9];
#endif

#ifdef SCENE_USE_SPECULAR_ENV
    samplerCube scene_EnvSpecularSampler;
#endif
```

### Normal
Normal calculation utilities:
```glsl
vec3 getNormalByNormalTexture(mat3 tbn, sampler2D normalTexture, float normalIntensity, vec2 uv, bool isFrontFacing);
mat3 getTBNByDerivatives(vec2 uv, vec3 normal, vec3 position, bool isFrontFacing);
```

### Shadow
Shadow-related functions ([source](https://github.com/galacean/engine/blob/main/packages/shader-shaderlab/src/shaders/Shadow.glsl)):
```glsl
int computeCascadeIndex(vec3 positionWS);
vec3 getShadowCoord(vec3 positionWS);
float sampleShadowMap(vec3 positionWS, vec3 shadowCoord);
```

### Skin
Skinning calculation:
```glsl
mat4 getSkinMatrix(Attributes attributes);
```

### BlendShape
Blend shape calculation:
```glsl
void calculateBlendShape(Attributes attributes, inout vec4 position, inout vec3 normal, inout vec4 tangent);
```

## PBR APIs

### AttributesPBR
All PBR attribute variables ([source](https://github.com/galacean/engine/blob/main/packages/shader-shaderlab/src/shaders/shadingPBR/AttributesPBR.glsl)).

### VaryingsPBR
All PBR varying variables ([source](https://github.com/galacean/engine/blob/main/packages/shader-shaderlab/src/shaders/shadingPBR/VaryingsPBR.glsl)).

### LightDirectPBR
Direct lighting calculations based on BRDF ([source](https://github.com/galacean/engine/blob/main/packages/shader-shaderlab/src/shaders/shadingPBR/LightDirectPBR.glsl)).

**Usage:**
```glsl
evaluateDirectRadiance(varyings, surfaceData, brdfData, shadowAttenuation, color.rgb);
```

**Override Macros:**
```glsl
#define FUNCTION_SURFACE_SHADING surfaceShading
#define FUNCTION_DIFFUSE_LOBE diffuseLobe
#define FUNCTION_SPECULAR_LOBE specularLobe
#define FUNCTION_CLEAR_COAT_LOBE clearCoatLobe
#define FUNCTION_SHEEN_LOBE sheenLobe

// Function signatures...
```

<Callout type="info">See PBR template extension example above.</Callout>

### LightIndirectPBR
Indirect lighting (IBL) calculations ([source](https://github.com/galacean/engine/blob/main/packages/shader-shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl)).

**Usage:**
```glsl
evaluateIBL(varyings, surfaceData, brdfData, color.rgb);
```

**Override Macros:**
```glsl
#define FUNCTION_DIFFUSE_IBL evaluateDiffuseIBL
#define FUNCTION_SPECULAR_IBL evaluateSpecularIBL
#define FUNCTION_CLEAR_COAT_IBL evaluateClearCoatIBL
#define FUNCTION_SHEEN_IBL evaluateSheenIBL

// Function signatures...
```

### VertexPBR
Vertex shader utilities for UV/TBN/skinning ([source](https://github.com/galacean/engine/blob/main/packages/shader-shaderlab/src/shaders/shadingPBR/VertexPBR.glsl)):

```glsl showLineNumbers {2, 4}
Varyings varyings;
varyings.uv = getUV0(attributes);

VertexInputs vertexInputs = getVertexInputs(attributes);

// positionWS
varyings.positionWS = vertexInputs.positionWS;

// normalWS、tangentWS、bitangentWS
#ifdef RENDERER_HAS_NORMAL
  varyings.normalWS = vertexInputs.normalWS;
  #ifdef RENDERER_HAS_TANGENT
    varyings.tangentWS = vertexInputs.tangentWS;
    varyings.bitangentWS = vertexInputs.bitangentWS;
  #endif
#endif

gl_Position = renderer_MVPMat * vertexInputs.positionOS;
```

### BRDF
Core PBR lighting calculations and data structures ([source](https://github.com/galacean/engine/blob/main/packages/shader-shaderlab/src/shaders/shadingPBR/BRDF.glsl)).

### BTDF
Transmission/refraction functions ([source](https://github.com/galacean/engine/blob/main/packages/shader-shaderlab/src/shaders/shadingPBR/BTDF.glsl)).

### FragmentPBR
Handles material properties and surface data initialization ([source](https://github.com/galacean/engine/blob/main/packages/shader-shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl)):

```glsl showLineNumbers
BRDFData brdfData;

SurfaceData surfaceData = getSurfaceData(varyings, aoUV, gl_FrontFacing);
initBRDFData(surfaceData, brdfData);
```

## Final Notes

For complete file organization examples, refer to the official [ForwardPassPBR](https://github.com/galacean/engine/blob/main/packages/shader-shaderlab/src/shaders/shadingPBR/ForwardPassPBR.glsl) implementation.